package pro.cherish.knowledgedemoioc.util;


import org.apache.commons.beanutils.MethodUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ClassUtils;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;


public class SpringExtensionHelper {

    private static Logger logger = LoggerFactory.getLogger(SpringExtensionHelper.class);

    private static Class<?> springLoaderClass = null;

    private static final String SPRING_EXTENSION_LOADER_CLASS = "pro.cherish.iocdemo.util.SpringUtil";

    /**
     * 初始化Spring注解属性字段
     *
     * @param instance
     */
    public static void initAutowireFields(Object instance) {
        if (springLoaderClass == null) {
            try {
                springLoaderClass = ClassUtils.forName(SPRING_EXTENSION_LOADER_CLASS, Thread.currentThread().getContextClassLoader());
            } catch (Throwable t) {
                logger.warn("Can't load class {}", SPRING_EXTENSION_LOADER_CLASS, t);
            }
        }

        if (springLoaderClass != null) {
            Field[] fields = getAnnotatedFields(instance.getClass(), Resource.class);
            for (Field field : fields) {
                try {
                    Object value = null;
                    String name = field.getAnnotation(Resource.class).name();
                    if (name == null || "".equals(name)) {
                        // Method getBeanByType = springLoaderClass.getMethod("getBean", field.getType());
                        // value = MethodUtil.invoke(getBeanByType, instance, new Class[]{field.getType()});
                        value = MethodUtils.invokeStaticMethod(springLoaderClass, "getBean", field.getType());
                    } else {
                        // Method getBeanByName = springLoaderClass.getMethod("getBean", String.class);
                        // value = MethodUtil.invoke(getBeanByName, instance, new Object[]{field.get(springLoaderClass)});
                        value = MethodUtils.invokeStaticMethod(springLoaderClass, "getBean", name);
                    }
                    if (value != null) {
                        field.setAccessible(true);
                        field.set(instance, value);
                    }
                } catch (Exception e) {
                    logger.error("Set spring bean to field " + field.getDeclaringClass() + "." + field.getName() + " error!", e);
                }
            }
        }
    }

    public static Field[] getAnnotatedFields(Class clazz, Class annotation) {
        List<Field> fieldList = new ArrayList();
        Field[] fields = getAllFields(clazz, 0);
        Field[] arr = fields;
        int len = fields.length;

        for (int i = 0; i < len; ++i) {
            Field field = arr[i];
            if (field.isAnnotationPresent(annotation)) {
                fieldList.add(field);
            }
        }

        return (Field[]) ((Field[]) fieldList.toArray(new Field[fieldList.size()]));
    }

    public static Field[] getAllFields(Class r_Class, int r_Filer) {
        List t_List = new ArrayList();

        for (Class t_Class = r_Class; null != t_Class; t_Class = t_Class.getSuperclass()) {
            addAll(t_List, getFields(t_Class, r_Filer));
        }

        Field[] t_ResultFields = new Field[t_List.size()];
        t_List.toArray(t_ResultFields);
        return t_ResultFields;
    }

    private static void addAll(Collection r_Collection, Object[] r_Objects) {
        if (null != r_Objects) {
            for (int i = 0; i < r_Objects.length; ++i) {
                if (null != r_Objects[i]) {
                    r_Collection.add(r_Objects[i]);
                }
            }

        }
    }

    public static Field[] getFields(Class r_Class, int r_Filer) {
        List t_List = new ArrayList();
        Field[] t_Fields = r_Class.getDeclaredFields();

        for (int i = 0; i < t_Fields.length; ++i) {
            Field t_Field = t_Fields[i];
            if ((t_Field.getModifiers() & r_Filer) == 0) {
                t_List.add(t_Field);
            }
        }

        Field[] t_ResultFields = new Field[t_List.size()];
        t_List.toArray(t_ResultFields);
        return t_ResultFields;
    }

}
